/*******************************************************************************
 * Copyright (c) 2016 Ericsson and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/

package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch;

import java.util.List;

import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.StackFramesVMNode;
import org.eclipse.cdt.dsf.gdb.internal.service.IGDBFocusSynchronizer.IGDBFocusChangedEvent;
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate;
import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;

public class GdbStackFramesVMNode extends StackFramesVMNode {

	public GdbStackFramesVMNode(AbstractDMVMProvider provider, DsfSession session) {
		super(provider, session);
	}

	@Override
	public int getDeltaFlags(Object e) {
		if (e instanceof IGDBFocusChangedEvent) {
        	return IModelDelta.SELECT;
		}
		
		return super.getDeltaFlags(e);
	}
	
	@Override
	public void buildDelta(final Object e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) {
		if (e instanceof IGDBFocusChangedEvent) {
			buildDeltaForFocusChangedEvent((IGDBFocusChangedEvent)e, parentDelta, rm);
		}
		else {
			super.buildDelta(e, parentDelta, nodeOffset, rm);
		}
	}
	
	private void buildDeltaForFocusChangedEvent(IGDBFocusChangedEvent event, VMDelta parentDelta, RequestMonitor rm) {
		getSession().getExecutor().execute(new Runnable() {
			@Override
			public void run() {
				IDMContext ctx = event.getDMContext();
				
				// Is IGDBFocusChangedEvent pertinent for this VMNode? 
				if (ctx instanceof IFrameDMContext) {
					IFrameDMContext newFrameFocus = (IFrameDMContext)ctx;
					IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(newFrameFocus, IMIExecutionDMContext.class);
					if (execDmc == null) {
						rm.done();
						return;
					}

    	            IRunControl runControl = getServicesTracker().getService(IRunControl.class); 
    	            if (runControl == null) {
    	                // Required services have not initialized yet.  Ignore the event.
    	                rm.done();
    	                return;
    	            }         

					if (runControl.isSuspended(execDmc) || runControl.isStepping(execDmc)) {
						// find the VMC index for the frame that switched, so we can select it correctly.
						getVMCIndexForDmc(
								GdbStackFramesVMNode.this,
								newFrameFocus,
								parentDelta,
								new DataRequestMonitor<Integer>(getExecutor(), rm) {
									@Override
									protected void handleSuccess() {
										// change to frameOffset
										final int frameOffset = getData();

										// Retrieve the list of stack frames
										getVMProvider().updateNode(GdbStackFramesVMNode.this,
												new VMChildrenUpdate(parentDelta,
														getVMProvider().getPresentationContext(), -1,
														-1, new DataRequestMonitor<List<Object>>(
																getExecutor(), rm) {
													@Override
													public void handleSuccess() {
														final List<Object> data = getData();
														if (data != null && data.size() != 0) {
															// create the delta to select the
															// current stack frame
															parentDelta.addNode(
																	data.get(frameOffset),
																	frameOffset,
																	IModelDelta.SELECT | IModelDelta.FORCE
																	);
														}
														rm.done();
													}
												}));
									}
								});
					} else {
						// thread is running - no delta to produce for the stack frame node
						rm.done();
					}
				} else {
					// context not a frame  - nothing to do here
					rm.done();
				}
			}
    	});
	}
}
